/**
 * device.c: Implements simulated devices.
 *
 * Author: 	Nora Tarano <ntarano@andrew.cmu.edu>
 *          Yuan Meng <yuanm@andrew.cmu.edu>
 *          Yipeng Yun <yipengy@andrew.cmu.edu>
 * Date: 	Dec 11, 2011
 */

#include <types.h>
#include <assert.h>

#include <task.h>
#include <sched.h>
#include <device.h>

#include <bits/errno.h>

#include <arm/interrupt.h>
#include <arm/reg.h>
#include <arm/psr.h>
#include <arm/exception.h>

#define MAX_INT   0xFFFFFFFF

/**
 * @brief Fake device maintainence structure.
 * Since our tasks are periodic, we can represent 
 * tasks with logical devices. 
 * These logical devices should be signalled periodically 
 * so that you can instantiate a new job every time period.
 * Devices are signaled by calling dev_update 
 * on every timer interrupt. In dev_update check if it is 
 * time to create a tasks new job. If so, make the task runnable.
 * There is a wait queue for every device which contains the tcbs of
 * all tasks waiting on the device event to occur.
 */
 
struct dev
{
	tcb_t* sleep_queue;
	unsigned long   next_match;
};
typedef struct dev dev_t;

/* devices will be periodically signaled at the following frequencies */
const unsigned long dev_freq[NUM_DEVICES] = {100, 200, 500, 50};
static dev_t devices[NUM_DEVICES];

/**
 * @brief Initialize the sleep queues and match values for all devices.
 */
void dev_init(void)
{
  int i;
  for(i=0; i<NUM_DEVICES; i++)
  {
    devices[i].sleep_queue = 0;
    devices[i].next_match = dev_freq[i];
  }
}


/**
 * @brief Puts a task to sleep on the sleep queue until the corresponding
 * event is signalled.
 *
 * @param dev  Device number.
 */
void dev_wait(unsigned int dev)
{
	tcb_t *task = get_cur_tcb();
	
  // put task on the sleep queue 
  task->sleep_queue = devices[dev].sleep_queue;  
  devices[dev].sleep_queue = task;
  
  dispatch_sleep();                             
}


/**
 * @brief Signals the occurrence of an event on all applicable devices. 
 * This function should be called on timer interrupts to determine that 
 * the interrupt corresponds to the event frequency of a device. If the 
 * interrupt corresponded to the interrupt frequency of a device, this 
 * function should ensure that the task is made ready to run.
 */
void dev_update(unsigned long millis)
{
  tcb_t *task;
  int resche = 0;
  
  int i;
  for(i=0;i<NUM_DEVICES; i++)
  {
    //check if device has passed one period
  	if(millis >= devices[i].next_match)
    {
      devices[i].next_match += dev_freq[i];
      task = devices[i].sleep_queue;
      
      
      // add tasks to run queue
      while(task)
      {
        runqueue_add(task, task->cur_prio);
        task = task->sleep_queue;
        resche = 1;
      }
      
      //"empty" sleep queue 
      devices[i].sleep_queue = 0; 
    }
  }

  if(resche)
      request_reschedule();
}

